﻿
Imports std

Public Class envelope
    Implements equalBytes, ICloneable

    Public Shared ReadOnly empty As envelope = Nothing

    Private Class types
        Public Const v As String = character.v
        Public Const m As String = character.m
        Public Shared ReadOnly vl As Int32 = strlen(v)
        Public Shared ReadOnly ml As Int32 = strlen(m)
    End Class

    Private ReadOnly im As map(Of String, fromBytes) = Nothing
    Private ReadOnly om As map(Of String, toBytes) = Nothing
    Private ReadOnly iv As vector(Of fromBytes) = Nothing
    Private ReadOnly ov As vector(Of toBytes) = Nothing

    Private Function eva(Of T)(ByVal i As T, ByVal n As Boolean) As T
        If n Then
            Return Global.copy(i)
        Else
            Return i
        End If
    End Function

    Private Sub New(ByVal im As map(Of String, fromBytes),
                    ByVal om As map(Of String, toBytes),
                    ByVal iv As vector(Of fromBytes),
                    ByVal ov As vector(Of toBytes),
                    ByVal cp As Boolean)
        Me.im = eva(im, cp)
        Me.om = eva(om, cp)
        Me.iv = eva(iv, cp)
        Me.ov = eva(ov, cp)
    End Sub

    Public Sub New()
        Me.New(New map(Of String, fromBytes)(),
               New map(Of String, toBytes)(),
               New vector(Of fromBytes)(),
               New vector(Of toBytes)(),
               False)
    End Sub

    Public Function output_vector_size() As Int64
        Return ov.size()
    End Function

    Public Function add_vector(ByVal i As toBytes) As Boolean
        Return ov.push_back(i)
    End Function

    Public Function add(ByVal i As toBytes) As Boolean
        Return add_vector(i)
    End Function

    Public Function output_map_size() As Int64
        Return om.size()
    End Function

    Public Function add_map(ByVal n As String, ByVal i As toBytes) As Boolean
        If om.find(n) = om.end() Then
            Return om.insert(n, i) <> om.end()
        Else
            Return False
        End If
    End Function

    Public Function add(ByVal n As String, ByVal i As toBytes) As Boolean
        Return add_map(n, i)
    End Function

    Public Function input_vector_size() As Int64
        Return iv.size()
    End Function

    Private Function get_cast(Of T)(ByVal g As _do(Of fromBytes, Boolean), ByRef o As T) As Boolean
        assert(Not g Is Nothing)
        Dim i As fromBytes = Nothing
        Return g(i) AndAlso
               Not i Is Nothing AndAlso
               i.as(o)
    End Function

    Public Function get_vector(ByVal index As Int64, ByRef o As fromBytes) As Boolean
        Return iv.take(index, o)
    End Function

    Public Function get_vector(Of T)(ByVal index As Int64, ByRef o As T) As Boolean
        Return get_cast(Function(ByRef x) get_vector(index, x), o)
    End Function

    Public Function [get](ByVal index As Int64, ByRef o As fromBytes) As Boolean
        Return get_vector(index, o)
    End Function

    Public Function [get](Of T)(ByVal index As Int64, ByRef o As T) As Boolean
        Return get_vector(index, o)
    End Function

    Public Function input_map_size() As Int64
        Return im.size()
    End Function

    Public Function input_map_keys() As vector(Of String)
        Dim rtn As vector(Of String) = Nothing
        rtn = New vector(Of String)()
        Dim it As map(Of String, fromBytes).iterator = Nothing
        it = im.begin()
        While it <> im.end()
            rtn.push_back((+it).first())
            it += 1
        End While
        Return rtn
    End Function

    Public Function get_map(ByVal name As String, ByRef o As fromBytes) As Boolean
        Dim it As map(Of String, fromBytes).iterator = Nothing
        it = im.find(name)
        If it <> im.end() Then
            o = (+it).second
            Return True
        Else
            Return False
        End If
    End Function

    Public Function get_map(Of T)(ByVal name As String, ByRef o As T) As Boolean
        Return get_cast(Function(ByRef x) get_map(name, x), o)
    End Function

    Public Function [get](ByVal name As String, ByRef o As fromBytes) As Boolean
        Return get_map(name, o)
    End Function

    Public Function [get](Of T)(ByVal name As String, ByRef o As T) As Boolean
        Return get_map(name, o)
    End Function

    Public Sub frombytes(ByVal b As bytes) Implements frombytes.frombytes
        Dim fms() As fromBytes = Nothing
        Dim des_rtn As Boolean = False
        des_rtn = deserialize(b, fms)
        If des_rtn AndAlso Not fms Is Nothing Then
            Dim i As Int64 = 0
            While i < fms.Length()
                Dim n As String = Nothing
                n = fms(i).as(Of String)()
                If isemptystring(n) Then
                    raiseError(errorHandle.errorType.warning,
                               "fromBytes(", i, ") is an empty string, ignore the field.")
                ElseIf strstartwith(n, types.m) Then
                    im(strmid(n, types.ml)) = fms(_inc(i))
                ElseIf strstartwith(n, types.v) Then
                    Dim c As Int64 = 0
                    If convertor.toint64(strmid(n, types.vl), c) Then
                        For j As Int64 = 0 To c - 1
                            iv.push_back(fms(_inc(i)))
                        Next
                    Else
                        raiseError(errorHandle.errorType.warning,
                                   "fromBytes(", i, ") is not an expected ", types.v, " format [", n, "]")
                    End If
                Else
                    raiseError(errorHandle.errorType.warning,
                               "fromBytes(", i, ") is not an expected string with type information, ignore the field.")
                End If
                i += 1
            End While
        Else
            Dim msg As String = "cannot deserialize from bytes, ex "
            If Not des_rtn Then
                msg = strcat(msg, "deserialize returns false.")
            ElseIf fms Is Nothing Then
                msg = strcat(msg, "output fromBytes() is nothing.")
            Else
                assert(False)
            End If
            raiseError(errorHandle.errorType.warning, msg)
        End If
    End Sub

    Public Function tobytes() As bytes Implements tobytes.tobytes
        If ov.size() = 0 AndAlso om.size() = 0 Then
            raiseError(errorHandle.errorType.warning, "no output data provided, ignore request.")
            Return Nothing
        End If

        Dim bss() As bytes = Nothing
        Dim j As Int64 = 0
        If ov.size() > 0 Then
            ReDim bss(ov.size() + 1 + (om.size() << 1) - 1)
            Dim n As String = Nothing
            n = strcat(types.v, ov.size())
            If Not serialize(bss(inc_(j)), New bytesString(n)) Then
                raiseError(errorHandle.errorType.warning,
                           "failed to serialize name of vector ", n, ", ignore request.")
                Return Nothing
            End If
            For i As Int64 = 0 To ov.size() - 1
                If Not serialize(bss(inc_(j)), ov(i)) Then
                    raiseError(errorHandle.errorType.warning,
                               "failed to serialize vector@", i, ", original data is ", ov(i), ", ignore request.")
                    Return Nothing
                End If
            Next
        Else
            ReDim bss((om.size() << 1) - 1)
        End If

        Dim it As map(Of String, toBytes).iterator = Nothing
        it = om.begin()
        While it <> om.end()
            If Not serialize(bss(inc_(j)), New bytesString(strcat(types.m, (+it).first()))) Then
                raiseError(errorHandle.errorType.warning,
                           "failed to serialize name of map@", (+it).first(), ", ignore request.")
                Return Nothing
            End If
            If Not serialize(bss(inc_(j)), (+it).second) Then
                raiseError(errorHandle.errorType.warning,
                           "failed to serialize map@", (+it).first(),
                           ", original data is ", (+it).second, ", ignore request.")
                Return Nothing
            End If
            it += 1
        End While
        assert(j = bss.Length())

        Dim size As Int64 = 0
        For i As Int64 = 0 To bss.Length() - 1
            size += bss(i).size()
        Next

        Dim rtn As bytes = Nothing
        rtn = New bytes()
        rtn.size() = size
        j = 0
        For i As Int64 = 0 To bss.Length() - 1
            memcpy(+rtn, inc_(j, bss(i).size()), +bss(i))
        Next
        Return rtn
    End Function

    Public Function Clone() As Object Implements System.ICloneable.Clone
        Return New envelope(im, om, iv, ov, True)
    End Function
End Class
